package com.ys.多线程.p8集合与多线程同步;

import java.util.Arrays;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 描述: 自定义的 ArrayList集合
 *
 * @author CodeYang
 * @version 2021/1/6 0:45
 */
public class MyArrayList<E> {

    private Lock lck = new ReentrantLock(true);

    /**
     * 集合的实际大小
     */
    private int size;


    /**
     * ArrayList本质就是一个数组,只是本次都扩容了原长度的一半 10-15-22-32
     */
    private Object[] elementData;

    public MyArrayList() {
        this(10);
    }

    public MyArrayList(int cap) {
        elementData = new Object[cap];
    }

    /**
     *
     * @return 返回结合元素的实际大小
     */
    public int size() {
        return size;
    }

    /**
     *  根据索引下标获取元素
     * @param index 索引下标
     * @return 集合元素
     */
    public E get(int index ){
        if (index < 0 || index > size) {
            throw new ArrayIndexOutOfBoundsException("size:"+size+"index:"+index);
        }
        // 返回对应下标对的元素
        return (E) elementData[index];
    }

    /**
     * 添加元素到ArrayList的末尾
     * 1:判断是否需要扩容
     * 2:添加元素到集合末尾
     * @param element
     */
//    public void add(E element){
//        int len = elementData.length;
//        if (size >= len){
//            // 扩容  1+1/2 右移 除2的N次方
//            int newCap = len+(len>>1);
//            // 复制原有数组 在赋给原有的数组变量,改变地址指向
//            elementData = Arrays.copyOf(elementData,newCap);
//        }
//        // 将新元素添加到末尾
//        // 多个线程来访问时: 多个线程同时调用add方法会出现越界异常，使用同步确保线程安全
//            elementData[size++] = element;
//
//    }
    public void add(E element){
        /**
         * 多个线程同时改变共享资源数据的代码块，使用同步，确保同一时刻最多只有一个线程访问同步代码块
         * synchronized修饰的代码块叫做临界区(同一时刻只能有一个线程访问临界区的代码)
         * 同步优点：确保线程安全
         * 缺点：效率不高
         */
        lck.lock();
        try {
            // synchronized(this){
            // 也可以用Lock锁来实现
            int len = elementData.length;
            if (size >= len){
                // 扩容  1+1/2 右移 除2的N次方
                int newCap = len+(len>>1);
                // 复制原有数组 在赋给原有的数组变量,改变地址指向
                elementData = Arrays.copyOf(elementData,newCap);
            }
            // 将新元素添加到末尾
            // 多个线程来访问时: 多个线程同时调用add方法会出现越界异常，使用同步确保线程安全
            elementData[size++] = element;
            //  }
        }finally {
            lck.unlock();
        }



    }


    /**
     * 根据数组下标索引来删除元素
     * @param index
     */
    public void remove(int index ){
        if (index < 0 || index > size){
            throw new ArrayIndexOutOfBoundsException("size:"+size+"index:"+index);
        }
        // 计算有多少个元素需要挪动
        // 例如: size =17 需要删除下标为5的元素, 17-5-1 = 11 此时需要挪动11个元素
        int numMoved = size - index -1;
        if (numMoved > 0){
            //挪动数组元素
            /**
             * 参数1: 原始数组
             * 参数2:原始数组开始拷贝的索引位置,表示从index+1 后面的所有原始的集和
             * 参数3:目标数组
             * 参数4:目标数组开始覆盖的位置
             * 参数5:需要覆盖(移动)元素的个数
             */
            System.arraycopy(elementData,index+1,elementData,index,numMoved);
        }
        // 情空末尾元素
        elementData[--size] = null;

    }

    @Override
    public String toString() {
        return Arrays.toString(elementData);
    }
}
